package com.navinfo.tripanalysis.common.util;

import com.navinfo.tripanalysis.common.arithmetic.exception.RangeAssert;
import org.apache.commons.collections.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import static java.math.BigDecimal.ROUND_HALF_UP;

/**
 * 基础算法类集合.
 *
 * @author 水哥
 */
public class ArithmeticUtils {

    private ArithmeticUtils() {
    }

    /**
     * 根据经纬度获取区域编码
     *
     * @param lat           纬度
     * @param lon           经度
     * @param tileLcMapping 瓦片与区域映射表
     * @return 区域ID
     */
    public static Integer getLc(int lat, int lon, Map<Long, Integer> tileLcMapping) {
        if (tileLcMapping == null) {
            return null;
        }
        Long tileIdMin = CommonUtils.getTileNumber(lat, lon, 15);
        Integer value = tileLcMapping.get(tileIdMin);
        return value;
    }

    /**
     * 加速度算法.
     *
     * @param preSpeedMs     上一个节点速度，单位m/s
     * @param currentSpeedMs 当前节点速度，单位m/s
     * @param millisecond    时间差，单位毫秒
     * @return Double 加速度
     */
    public static Double calculateAcceleratedSpeed(double preSpeedMs, double currentSpeedMs, long millisecond) {
        RangeAssert.speedIsInTheRange(preSpeedMs);
        RangeAssert.speedIsInTheRange(currentSpeedMs);
        RangeAssert.millisecondIsInTheRange(millisecond);
        BigDecimal seconds = BigDecimalUtils.divide(millisecond, 1000, 2);
        if (preSpeedMs == 0 || seconds.equals(BigDecimal.ZERO)) {
            return 0d;
        }
        return BigDecimalUtils.divide(currentSpeedMs - preSpeedMs, seconds, 2).doubleValue();
    }

    /**
     * 速度换算，千米/小时 转成 m/s.
     *
     * @param speedKmh 速度千米/小时
     * @return 速度m/s
     */
    public static Double speedKmhToMs(int speedKmh) {
        RangeAssert.speedKmhIsInTheRange(speedKmh);
        return BigDecimalUtils.divide(BigDecimalUtils.multiply(speedKmh, 1000), 3600, 2).doubleValue();
    }

    /**
     * 速度换算，m/s 转成千米/小时 .
     *
     * @param speedMs 速度m/s
     * @return 速度千米/小时
     */
    public static int speedMsToKmh(double speedMs) {
        return BigDecimalUtils.divide(BigDecimalUtils.multiply(speedMs, 3600, 2), 1000, 2).setScale(1, ROUND_HALF_UP).intValue();
    }

    /**
     * 油耗单位转换, 毫升转ml
     *
     * @return
     */
    public static int fuelMlToMl(double ml) {
        return BigDecimalUtils.multiply(ml, 1, 0).intValue();
    }
    /**
     * 油耗单位转换, 升转ml
     *
     * @return
     */
    public static int fuelLToMl(double l) {
        return BigDecimalUtils.multiply(l, 1000, 0).intValue();
    }

    /**
     * 里程单位转换：double类型的米转为int类型的米
     *
     * @param m
     * @return
     */
    public static int mileageMToM(double m) {
        return BigDecimalUtils.multiply(m, 1.0, 0).intValue();
    }
    /**
     * 里程单位转换：double类型的千米转为int类型的米
     *
     * @param km 千米
     * @return 米
     */
    public static int mileageKMToM(double km) {
        return BigDecimalUtils.multiply(km, 1000, 0).intValue();
    }

    /**
     * 时长单位转换,毫秒转为秒
     *
     * @param ms
     * @return
     */
    public static int timeMsToSecond(int ms) {
        // 60*10
        return BigDecimalUtils.divide(ms, 1000, 0).intValue();
    }

    /**
     * 时长单位转换,毫秒转为秒
     *
     * @param ms
     * @return
     */
    public static Integer timeMsToSecond(Integer ms) {
        if (ms == null) {
            return null;
        }
        // 60*10
        return BigDecimalUtils.divide(ms, 1000, 0).intValue();
    }

    /**
     * 时长单位转换：double类型的秒为int类型的秒
     * @param s
     * @return
     */
    public static int timeSToS(double s) {
        return BigDecimalUtils.multiply(s, 1.0, 0).intValue();
    }

    /**
     * 时长单位转换,分钟转为毫秒
     *
     * @param minute
     * @return
     */
    public static int timeMinuteToMs(int minute) {
        // 60*10
        return minute * 60000;
    }

    /**
     * 计算标准差.
     *
     * @param collection double集合
     * @return double 标准差
     */
    public static double standardDeviation(Collection<Double> collection) {
        if (CollectionUtils.isEmpty(collection)) {
            throw new IllegalArgumentException("标准差算法参数错误 collection can not be empty!");
        }
        double sum = 0;
        double avg = getAverage(collection);
        for (Double i : collection) {
            sum += (i - avg) * (i - avg);
        }
        return Math.sqrt(BigDecimalUtils.divide(sum, collection.size(), 2).doubleValue());
    }

    private static double getAverage(Collection<Double> collection) {
        int num = collection.size();
        double sum = 0;
        for (Double i : collection) {
            sum += i;
        }
        return BigDecimalUtils.divide(sum, num, 2).doubleValue();
    }

    //计算和
    public static double calcSum(List<Integer> list){
        double sum = 0;
        for(int i = 0;i<list.size();i++){
            sum += list.get(i);
        }
        return sum;
    }

    //求平均值
    public static double mean(List<Integer> list){
        return calcSum(list) / list.size();
    }

    //求标准差
    public static  double standardDeviaction(List<Integer> list){
        double sum = 0;
        double meanValue = mean(list);                //平均数
        for(int i = 0;i < list.size();i++){
            sum += Math.pow(list.get(i)-meanValue, 2);
        }
        return Math.sqrt(sum / list.size());
    }


    /**
     * 占比四舍五入算法
     *
     * @param s
     * @return
     */
    public static int dealRatio(double s) {
        int value = BigDecimalUtils.divide(s, 1, 0).intValue();
        if (value < 0) {
            value = 0;
        } else if (value > 100) {
            value = 100;
        }
        return value;
    }

    /**
     * 计算百公里次数
     *
     * @param sumTimes
     * @param sumMileage 单位为米
     * @return
     */
    public static int hundredKmTimes(int sumTimes, int sumMileage) {
        if (sumTimes == 0 || sumMileage / 1000 == 0) {
            return 0;
        }
        return BigDecimal.valueOf(sumTimes * 100).divide(BigDecimal.valueOf(sumMileage / 1000), 4, BigDecimal.ROUND_HALF_UP).multiply(BigDecimal.valueOf(10000)).intValue();
    }

    /**
     * 计算时长占比
     *
     * @param duration
     * @param sumDuration
     * @return
     */
    public static int durationProportion(int duration, int sumDuration) {
        if (duration == 0 || sumDuration == 0) {
            return 0;
        }
        return BigDecimal.valueOf(duration).divide(BigDecimal.valueOf(sumDuration), 4, BigDecimal.ROUND_HALF_UP).multiply(BigDecimal.valueOf(10000)).intValue();
    }

    /**
     * 计算里程占比
     *
     * @param mileage
     * @param sumMileage
     * @return
     */
    public static int mileageProportion(int mileage, int sumMileage) {
        return durationProportion(mileage, sumMileage);
    }

    /**
     * 尿素喷射量(g)计算出毫升数(ml)
     */
    public static int massToVolume(double ureaInjection, int atmosphericTem) {

        double density = 0.00;
        //算法:计算密度:温度大于50
        if (atmosphericTem < 12) {
            density = 1097;
        } else if (atmosphericTem > 50) {
            density = 1072;
        } else {
            density = BigDecimal.valueOf(-0.658).multiply(BigDecimal.valueOf(atmosphericTem)).add(BigDecimal.valueOf(1104.9)).doubleValue();
        }
        return BigDecimalUtils.divide(BigDecimal.valueOf(ureaInjection).multiply(BigDecimal.valueOf(1000)).doubleValue(), density, 0).intValue();
    }
}
